#include "FreeWrite.h"
#include "FontMap.h"


namespace freewrite
{

FontMap::FontMap(IFWTexture2D* pTexture)
	: m_Texture(pTexture)
{
	char id[256];
	FW_sprintf(id, sizeof(id), "Runtime.DynamicTexture2D.FontBitmap_%x", static_cast<void*>(this));

	AllocMap2D* pUpper = nullptr;
	for(int32_t dim = 1; dim * FW_SMALLEST_GRID_DIM <= FW_TEXTURE_DIM; dim *= 2)
	{
		m_MipChain.insert(m_MipChain.begin(), new AllocMap2D(dim, pUpper));
		pUpper = m_MipChain.front();
	}
}

FontMap::~FontMap()
{
	for(size_t i=0; i<m_MipChain.size(); i++)
	{
		AllocMap2D* pMap = m_MipChain[i];
		FW_SAFE_DELETE(pMap);
	}
	FW_SAFE_DELETE(m_Texture);
}

bool FontMap::Alloc(Character* chr, AllocationToken* token)
{
	//FIXME: Optimization, allow half square
	//if(bh < bw / 2)
	//{ bh = bw / 2; }
	//else if(bw < bh / 2)
	//{ bw = bh / 2; }

	chr->m_FontMap = this;
	chr->m_MipLevel = INT16_MAX;
	uint32_t dim = std::max(chr->m_BmpWidth, chr->m_BmpHeight);
	for(size_t i=0; i < m_MipChain.size(); i++)
	{
		if(dim <= m_MipChain[i]->m_GridSize)
		{
			chr->m_MipLevel = i;
			break;
		}
	}

	if((size_t)chr->m_MipLevel >= m_MipChain.size())
		return false;

	if(!m_MipChain[chr->m_MipLevel]->Alloc(chr->m_Index))
	{
		return false;
	}

	Vector2I16 offset = m_MipChain[chr->m_MipLevel]->IndexToXY(chr->m_Index);
	Vector2I16 texelOffset = m_MipChain[chr->m_MipLevel]->XYToTexelOffset(offset.X, offset.Y);
	token->TexelOffset = texelOffset;
	token->FontMap = this;
	return true;
}

bool FontMap::WriteBitmap(uint8_t* pSrc, uint32_t srcPitch, uint32_t width, uint32_t height, AllocationToken* token, bool bDoNotWait)
{
	return m_Texture->SetData(pSrc, srcPitch, (uint32_t)token->TexelOffset.X, (uint32_t)token->TexelOffset.Y, width, height, bDoNotWait);
}

Vector4U16 FontMap::CalculateSubUV(const Character* pChr) const
{
	assert(pChr->m_FontMap == this);
	Vector2I16 offset = m_MipChain[pChr->m_MipLevel]->IndexToXY(pChr->m_Index);
	Vector2I16 texelOffset = m_MipChain[pChr->m_MipLevel]->XYToTexelOffset(offset.X, offset.Y);
	
	Vector4U16 subUV;
	subUV.X = (uint16_t)(std::max(0, std::min<int32_t>(UINT16_MAX, pChr->m_BmpWidth * UINT16_MAX / FW_TEXTURE_DIM)));
	subUV.Y = (uint16_t)(std::max(0, std::min<int32_t>(UINT16_MAX, pChr->m_BmpHeight * UINT16_MAX / FW_TEXTURE_DIM)));
	subUV.Z = (uint16_t)(std::max(0, std::min<int32_t>(UINT16_MAX, texelOffset.X * UINT16_MAX / FW_TEXTURE_DIM)));
	subUV.W = (uint16_t)(std::max(0, std::min<int32_t>(UINT16_MAX, texelOffset.Y * UINT16_MAX / FW_TEXTURE_DIM)));
	return subUV;
}

}